#include "pch.hpp"
#include "snake.hpp"
#include "item.h"
#include "animation.h"
#include "hud.h"

using namespace std;

extern std::vector<item> items;
static sf::Vector2f itemPos;
static animation snake_anim;
static animation snake_blink_anim;
static sf::Clock snake_clock;
static animation snake_head_burn;
static animation luska_dead;

const float NEXT_BALL_HIDE_TIME = 0.025f;

void OnEraseBall(sf::Vector2f pos, BallType::Type type);

float WrapAngle180(float angle)
{
	while(angle<-180)
		angle += 360;
	while(angle>180)
		angle -= 360;

	return angle;
}

CSnake::CSnake(void)
	: fEatingInterval(8.0f), fMovementSpeed(0.1f), fHeadAngle(0.0f),
	bEating(false), fEatingTime(0.0f), fSlowedTime(0.0f), fFrozenTime(0.0f),
	bSnakeDead(false), bSnakeDone(false), iCurrentLevel(1), iBallCount(3)
{}

CSnake::CSnake(int iInitialLength, int iLevel)
	: fHeadAngle(0.0f), bEating(false), fEatingTime(0.0f), fSlowedTime(0.0f),
	fFrozenTime(0.0f), bSnakeDead(false), bSnakeDone(false), iCurrentLevel(iLevel)
{
	if (iCurrentLevel == 1)
	{
		iBallCount = 3;
		fEatingInterval = 8.0f;
		fMovementSpeed = 0.1f;
	}
	else if (iCurrentLevel == 2)
	{
		iBallCount = 4;
		fEatingInterval = 6.0f;
		fMovementSpeed = 0.13f;
	}
	else
	{
		iBallCount = 5;
		fEatingInterval = 3.0f + 2 / (iCurrentLevel - 2);
		fMovementSpeed = 0.1f + 0.03f * (iCurrentLevel - 1);
	}
	int type;
	for (int i = 0 ; i < iInitialLength ; ++i)
	{
		if (i != 10)
		{
			type = rand() % iBallCount;
		}
		switch (type)
		{
			case 0:
				vTail.push_back(BallType::Red);
				break;
			case 1:
				vTail.push_back(BallType::Green);
				break;
			case 2:
				vTail.push_back(BallType::Blue);
				break;
			case 3:
				vTail.push_back(BallType::Yellow);
				break;
			case 4:
				vTail.push_back(BallType::Violet);
				break;
		}
		vTailSprite.push_back(sf::Sprite());
		vScaleSprite.push_back(sf::Sprite());
		vScaleSprite.push_back(sf::Sprite());
	}
	vTailAngles.resize(iInitialLength + 1);
}

void CSnake::Load(void)
{
	oHeadSprite.SetImage(oHeadImage);
	oHeadSprite.SetCenter((float)(oHeadImage.GetWidth() / 2), (float)(oHeadImage.GetHeight() / 2));
	stringstream ssFormat;
	string strTmp;
	fBallRadius = (float)GetBallImage(BallType::Red).GetWidth() * 0.5f;
	for (int i = 0 ; i < vTailSprite.size() ; ++i)
	{
		vTailSprite[i].SetCenter((float)(GetBallImage(BallType::Red).GetWidth() / 2.0f),
			(float)(GetBallImage(BallType::Red).GetHeight() / 2.0f));
	}
	float fCircleRadius = fBallRadius * ((float)vTail.size() + 1.0f) / PI;
	float fDeltaAngle = 360.0f / ((float)vTail.size() + 1.0f);
	vTailAngles[0] = fHeadAngle + vTail.size() * fDeltaAngle;
	oHeadSprite.SetRotation(vTailAngles[0]);
	for (int i = 0 ; i < vTail.size() ; ++i)
	{
		int a = vTail.size() - i - 1;
		vTailAngles[a + 1] = fHeadAngle + i * fDeltaAngle;
		//vTailSprite[a].SetRotation(vTailAngles[a + 1]);
		vTailSprite[a].SetPosition(cos(DEGTORAD(-(fHeadAngle + i * fDeltaAngle))) * fCircleRadius,
			384.0f + sin(DEGTORAD(-(fHeadAngle + i * fDeltaAngle))) * fCircleRadius);
	}
	snake_anim.load("data/head_eat_resize_v2.png", 100, 100, 6, 100, false, 0.83f);
	snake_blink_anim.load("data/blink_new_resize_head_this_v2.png", 100, 100, 4, 100, false, 0.83f);
	oScaleImage.LoadFromFile("data/luska.png");
	sf::Vector2f center(oScaleImage.GetWidth() / 2.0f,
		oScaleImage.GetHeight() / 2.0f);
	for (int i = 0 ; i < vScaleSprite.size() ; ++i)
	{
		vScaleSprite[i].SetImage(oScaleImage);
		vScaleSprite[i].SetCenter(center);
	}
	bLengthening = false;
	bSnakeDead = bHamsterDead = false;
	iVisibleBallsCount = 53;
	fSnakeAlpha = 1.0f;
	snake_head_burn.load("data/snake_head_burn.png", 100, 100, 6, 111, false);
	luska_dead.load("data/luska_dead.png", 75, 44, 6, 111, false);
	topisie = false;
}

void CSnake::Update(float fDelta)
{
	float fSlowFreezeFactor = 1.0f;
	if (fSlowedTime > 0.0f)
	{
		fSlowFreezeFactor = 0.3f;
	}
	fSlowedTime -= fDelta;
	fFrozenTime -= fDelta;
	if (fFrozenTime <= 0.0f)
	{
		fEatingTime += fDelta;
	}
	if (fEatingTime >= fEatingInterval)
	{
		playSound("eatBall");
		fEatingTime -= fEatingInterval;
		bEating = true;
		snake_clock.Reset();
		fEatingAngle = 360.0f / ((float)vTail.size() + 1.0f);
	}
	float fCircleRadius = fBallRadius * ((float)vTail.size() + 1.0f) / PI;
	iVisibleBallsCount = 9999999;
	if (fCircleRadius >= 276.0f)
	{
		fNextBallHideTime = NEXT_BALL_HIDE_TIME;
		oSnakeDeadClock.Reset();
		iVisibleBallsCount = vTail.size();
		bSnakeDead = true;
	}
	else if (fCircleRadius <= 50.0f)
	{
		playSound("hamsterDies");
		bHamsterDead = true;
		man.setSpecialFrame(2);
		woman.setSpecialFrame(3);
		woman.set_default_frame(3);
		man.set_default_frame(2);
	}
	float fDeltaAngle = 360.0f / ((float)vTail.size() + 1.0f);
	float fEatingFactor = 1.0f, fLengtheningFactor = 0.0f;
	if (bEating)
	{
		if (fFrozenTime <= 0.0f)
		{
			fEatingAngle -= 10.0f * fMovementSpeed * fSlowFreezeFactor;
			fEatingFactor = fEatingAngle / fDeltaAngle;
		}
	}
	if (bLengthening)
	{
		fLengtheningAngle += 10.0f * fMovementSpeed * fSlowFreezeFactor;
		fLengtheningFactor = fLengtheningAngle / fDeltaAngle;
	}
	fCircleRadius = fBallRadius * ((float)vTail.size() + fEatingFactor + fLengtheningFactor) / PI;
	fDeltaAngle = 360.0f / ((float)vTail.size() + fEatingFactor + fLengtheningFactor);
	if (fFrozenTime <= 0.0f)
	{
		fHeadAngle += fMovementSpeed * fSlowFreezeFactor;
	}
	vTailAngles[0] = fHeadAngle + vTail.size() * fDeltaAngle;
	oHeadSprite.SetRotation(vTailAngles[0]);
	oHeadSprite.SetPosition(cos(DEGTORAD(-(fHeadAngle + vTail.size() * fDeltaAngle))) * (fCircleRadius - 6.0f),
		384.0f + sin(DEGTORAD(-(fHeadAngle + vTail.size() * fDeltaAngle))) * (fCircleRadius - 6.0f));
	for (int i = 0 ; i < vTail.size() ; ++i)
	{
		int a = vTail.size() - i - 1;
		vTailAngles[a + 1] = fHeadAngle + i * fDeltaAngle;
		if (bLengthening && a >= iLengtheningTailIndex)
		{
			vTailAngles[a + 1] -= fLengtheningAngle;
		}
		//vTailSprite[a].SetRotation(vTailAngles[a + 1]);
		vTailSprite[a].SetPosition(cos(DEGTORAD(-(vTailAngles[a + 1]))) * fCircleRadius,
			384.0f + sin(DEGTORAD(-(vTailAngles[a + 1]))) * fCircleRadius);
	}

	for (int i = 0 ; i < vScaleSprite.size() ; ++i)
	{
		vScaleSprite[i].SetRotation((-90 + fHeadAngle + i * fDeltaAngle / 2.0f));
		vScaleSprite[i].SetPosition(cos(DEGTORAD(-(fHeadAngle + i * fDeltaAngle / 2.0f))) * fCircleRadius,
			384.0f + sin(DEGTORAD(-(fHeadAngle + i * fDeltaAngle / 2.0f))) * fCircleRadius);
	}

	if (bEating)
	{
		if (fEatingAngle <= 0.0f)
		{
			bEating = false;
			vTail.resize(vTail.size() - 1);
			vTailSprite.resize(vTailSprite.size() - 1);
			vTailAngles.resize(vTailAngles.size() - 1);
			vScaleSprite.resize(vScaleSprite.size() - 2);
			fHeadAngle += fDeltaAngle;

			if(vTail.size()<15)
			{
				man.setSpecialFrame(1);
				woman.setSpecialFrame(2);
			}
		}
	}
	if (bLengthening)
	{
		sf::Vector2f pos;
		if(iLengtheningTailIndex == 1000)
			pos = (vTailSprite.back().GetPosition()+oHeadSprite.GetPosition())/2.0f;
		else if(iLengtheningTailIndex == 0)
			pos = (vTailSprite[iLengtheningTailIndex].GetPosition()+oHeadSprite.GetPosition())/2.0f;
		else
			pos = (vTailSprite[iLengtheningTailIndex].GetPosition()+vTailSprite[iLengtheningTailIndex-1].GetPosition())/2.0f;

		float factor = 1-(fLengtheningAngle / (360.0f / ((float)vTail.size() + 1.0f)));

		items[iLengtheningItemIndex].setPosition(itemPos*factor+pos*(1-factor));

		if (fLengtheningAngle >= 360.0f / ((float)vTail.size() + 1.0f))
		{
			bLengthening = false;
			float angle = fLengtheningSavedAngle;
			if (iLengtheningTailIndex < 1000)
			{
				vTail.insert(vTail.begin() + iLengtheningTailIndex, items[iLengtheningItemIndex].getBallType());
				vTailSprite.insert(vTailSprite.begin() + iLengtheningTailIndex,
					sf::Sprite(GetBallImage(vTail[iLengtheningTailIndex])));
				vTailSprite[iLengtheningTailIndex].SetCenter(GetBallImage(BallType::Red).GetWidth() / 2.0f,
					GetBallImage(BallType::Red).GetWidth() / 2.0f);
				vTailAngles.insert(vTailAngles.begin() + iLengtheningTailIndex + 1, angle);
				vTailSprite[iLengtheningTailIndex].SetPosition(cos(DEGTORAD(vTailAngles[iLengtheningTailIndex + 1])) * fCircleRadius,
					384.0f + sin(DEGTORAD(vTailAngles[iLengtheningTailIndex + 1])) * fCircleRadius);
			}
			else
			{
				vTail.push_back(items[iLengtheningItemIndex].getBallType());
				vTailSprite.push_back(sf::Sprite(GetBallImage(items[iLengtheningItemIndex].getBallType())));
				vTailSprite.back().SetCenter(GetBallImage(BallType::Red).GetWidth() / 2.0f,
					GetBallImage(BallType::Red).GetWidth() / 2.0f);
				vTailAngles.push_back(angle);
				vTailSprite.back().SetPosition(cos(DEGTORAD(angle)) * fCircleRadius,
					384.0f + sin(DEGTORAD(angle)) * fCircleRadius);
			}

			score += pow((Iindex2 - Iindex1 + 1) * 20, 1.5);
			sf::Vector2f center(oScaleImage.GetWidth() / 2.0f,
				oScaleImage.GetHeight() / 2.0f);
			for (int i = Iindex1 ; i <= Iindex2 ; ++i)
			{
				if (i > 0 && i == Iindex1)
				{
					vTail[i] = vTail[i - 1];
				}
				else
				{
					switch (rand() % iBallCount)
					{
						case 0:
							vTail[i] = BallType::Red;
							break;
						case 1:
							vTail[i] = BallType::Green;
							break;
						case 2:
							vTail[i] = BallType::Blue;
							break;
						case 3:
							vTail[i] = BallType::Yellow;
							break;
						case 4:
							vTail[i] = BallType::Violet;
							break;
					}
				}
			}
			for (int i = 0 ; i < 2 ; ++i)
			{
				vScaleSprite.push_back(sf::Sprite());
				vScaleSprite.back().SetImage(oScaleImage);
				vScaleSprite.back().SetCenter(center);
			}
			items.erase(items.begin() + iLengtheningItemIndex);
			fHeadAngle -= fDeltaAngle;
		}
	}

	//

	if (!bLengthening)
	{
		for (int i = 0 ; i < items.size() ; ++i)
		{
			float fDistance = sfm::Length(sf::Vector2f(items[i].getPosition().x, items[i].getPosition().y - 384.0f));
			if (fDistance <= fCircleRadius + fBallRadius
				&& fDistance >= fCircleRadius)
			{
				float fMin1 = 400.0f, fMin2 = 400.0f;
				float x = items[i].getPosition().x, y = items[i].getPosition().y;
				y -= 768/2.0f;

				if(fDistance<1)
				{
					fDistance = 1;
					y = -1;
					x = 0;
				}

				float angle = acos(y/fDistance);
				if(x<0) angle = -angle;

				int index1 = -1, index2 = -1;
				for (int j = 0 ; j < vTailAngles.size() ; ++j)
				{
					float A = abs(WrapAngle180(RADTODEG(angle) - vTailAngles[j] - 90));
					if (A < fMin1)
					{
						fMin2 = fMin1;
						index2 = index1;
						fMin1 = A;
						index1 = j;
					}
					else if (A < fMin2)
					{
						fMin2 = A;
						index2 = j;
					}
				}
				if (index1 == -1 || index2 == -1)
				{
					cout << "Nie znaleziono odpowiednich kulek." << endl;
				}
				else
				{
					if (abs(index1 - index2) == 1)
					{
						iLengtheningTailIndex = std::min(index1, index2);
					}
					else
					{
						iLengtheningTailIndex = 1000;
					}

					BallType::Type tmpType;
					if(iLengtheningTailIndex == 1000)
					{
						tmpType = vTail.back();
						int nr = vTail.size() - 2;
						while (nr >= 0 && vTail[nr] == tmpType)
						{
							--nr;
						}
						Iindex1 = nr + 1;
						Iindex2 = vTail.size();
						iMatchCount = vTail.size() - 1 - nr;
					}
					else if(iLengtheningTailIndex == 0)
					{
						int nr = 0;
						tmpType = vTail[nr];
						while (vTail[nr] == tmpType)
						{
							++nr;
						}
						Iindex1 = 0;
						Iindex2 = nr;
						iMatchCount = nr;
					}
					else
					{
						int nr;
						if (vTail[iLengtheningTailIndex] == items[i].getBallType())
						{
							tmpType = vTail[iLengtheningTailIndex];
							nr = iLengtheningTailIndex;
							if (iLengtheningTailIndex < vTail.size() - 1)
							{
								nr = iLengtheningTailIndex + 1;
								while (nr < vTail.size() && vTail[nr] == tmpType)
								{
									++nr;
								}
								--nr;
							}
						}
						else
						{
							nr = iLengtheningTailIndex - 1;
							tmpType = vTail[nr];
						}
						int nr2 = iLengtheningTailIndex - 1;
						while (nr2 >= 0 && vTail[nr2] == tmpType)
						{
							--nr2;
						}
						Iindex1 = nr2 + 1;
						Iindex2 = nr + 1;
						iMatchCount = nr - nr2;
					}

					if (tmpType == items[i].getBallType() && iMatchCount >= 2)
					{
						playSound("addBall");
						fLengtheningSavedAngle = angle;
						bLengthening = true;
						iLengtheningItemIndex = i;
						itemPos = items[iLengtheningItemIndex].getPosition();
						fLengtheningAngle = 0.0f;
						break;
					}
				}
			}
		}
	}
}

void CSnake::Update_dead()
{
	fNextBallHideTime -= 1/60.0f;
	if(fNextBallHideTime <= 0)
	{
		if(iVisibleBallsCount)
		{
			playSound("explosion");
			iVisibleBallsCount--;
			fNextBallHideTime = NEXT_BALL_HIDE_TIME;
			if(iVisibleBallsCount>=0)
				OnEraseBall(vTailSprite[iVisibleBallsCount].GetPosition(), vTail[iVisibleBallsCount]);
			if(iVisibleBallsCount == 0)
			{
				oSnakeDeadClock.Reset();
				playSound("snakeKill");
				topisie = true;
			}
		}
	}

	if(iVisibleBallsCount == 0)
	{
		float Q = oSnakeDeadClock.GetElapsedTime();
		if(Q<0.666f)
			fSnakeAlpha = 0.666f - Q;
		else
		{
			fSnakeAlpha = 0;
			bSnakeDone = true;
		}
	}
}

void CSnake::Display(void)
{
	if(!topisie)
	{
		for (int i = 0 ; i < vScaleSprite.size() ; ++i)
		{
			vScaleSprite[i].SetColor(sf::Color(255,255,255,255*fSnakeAlpha));
			app.Draw(vScaleSprite[i]);
		}
		for (int i = 0 ; i < vTail.size() && i < iVisibleBallsCount; ++i)
		{
			vTailSprite[i].SetImage(GetBallImage(vTail[i]));
			app.Draw(vTailSprite[i]);
		}
		snake_anim.SetPos(oHeadSprite.GetPosition());
		snake_blink_anim.SetPos(oHeadSprite.GetPosition());
		sf::Vector2f dir(0, -1);
		sfm::Rotate(dir, -vTailAngles[0]-90);

		if (bEating)
		{
			snake_anim.draw((int)(snake_clock.GetElapsedTime()*1000), dir);
		}
		else if (snake_clock.GetElapsedTime() >= 2.0f)
		{
			if (snake_blink_anim.draw((int)((snake_clock.GetElapsedTime() - 2.0f)*1000), dir))
			{
				snake_clock.Reset();
			}
		}
		else
		{
			snake_blink_anim.draw(0, dir);
		}
	} else
	{
		for (int i = 0 ; i < vScaleSprite.size() ; ++i)
		{
			luska_dead.SetColor(sf::Color(255,255,255,255*fSnakeAlpha/0.666f));
			luska_dead.SetPos(vScaleSprite[i].GetPosition());
			luska_dead.SetRotation(vScaleSprite[i].GetRotation());
			luska_dead.draw(oSnakeDeadClock.GetElapsedTime()*1000, sf::Vector2f(0,0));
		}

		snake_head_burn.SetColor(sf::Color(255,255,255,255*fSnakeAlpha/0.666f));
		snake_head_burn.SetPos(oHeadSprite.GetPosition());
		snake_head_burn.SetRotation(oHeadSprite.GetRotation());
		sf::Vector2f dir(0, -1);
		sfm::Rotate(dir, -vTailAngles[0]-90);
		snake_head_burn.draw(oSnakeDeadClock.GetElapsedTime()*1000, dir);
	}
}

void CSnake::CheckForDeleted(int index)
{
	if (iLengtheningItemIndex > index)
	{
		--iLengtheningItemIndex;
	}
}

void CSnake::SlowDown(void)
{
	fSlowedTime = 4.0f;
}

void CSnake::Freeze(void)
{
	fFrozenTime = 4.0f;
}

bool CSnake::SnakeDied(void)
{
	return bSnakeDead;
}

bool CSnake::SnakeDone(void)
{
	return bSnakeDone;
}

bool CSnake::HamsterDied(void)
{
	return bHamsterDead;
}
